package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gomodule/redigo/redis"
	"io"
	"log"
	"math/rand"
	"os"
	"time"
)

var pool *redis.Pool
var sha1LuaScript *redis.Script

const (
	luaScript = `
		local number = ARGV[2]
		local num = redis.call('HGET', KEYS[1], ARGV[1])
		if not num or tonumber(num) <= 0 then
		  return -1
		end
	
		redis.call('HINCRBY', KEYS[1], ARGV[1], number)
		return 1
	`
)

type Goods struct {
	Id int `redis:"id"`
	Stock int `redis:"stock"`
}

func newPool(server string, auth string) *redis.Pool {
	return &redis.Pool{
		MaxIdle: 8, // 最大空闲链接
		MaxActive: 64, // 最大激活链接
		IdleTimeout: 5 * time.Second, // 最大空闲链接等待时间
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", server, redis.DialPassword(auth))
			if err != nil {
				return nil, err
			}
			return c, err
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			return err
		},
		Wait:true,
	}
}

func init() {
	pool = newPool("r-bp1p9ruvdd9o5l8pu8.redis.rds.aliyuncs.com:6379", "")

	conn := pool.Get()
	defer conn.Close()



	var goods Goods
	goods.Id = 1
	goods.Stock = 10000000
	if _, err := conn.Do("HMSET", redis.Args{}.Add("goods:1").AddFlat(&goods)...); err != nil {
		panic(err)
	}

	sha1LuaScript = redis.NewScript(1, luaScript)
}


func main() {
	cli, _ := redis.Dial("tcp", "r-bp1p9ruvdd9o5l8pu8.redis.rds.aliyuncs.com:6379")

	gin.SetMode(gin.ReleaseMode)
	f, _ := os.Create("gin.log")
	gin.DefaultWriter = io.MultiWriter(f)

	r := gin.Default()

	r.GET("/hGetAllNoPool", func(c *gin.Context) {

		values, err := redis.Values(cli.Do("HGETALL", "goods:1"))
		if err != nil {
			fmt.Println("HGETALL", err)
		}

		var goods Goods
		if err := redis.ScanStruct(values, &goods); err != nil {
			fmt.Println(err)
		}

		c.JSON(200, gin.H{
			"goods": goods,
		})
	})

	r.GET("/hGetAll", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"map": hGetAll(),
		})
	})
	r.GET("/hGetAll2", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"map1": hGetAll(),
			"map2": hGetAll(),
		})
	})

	r.GET("/setNX", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"setnx": setNX(),
		})
	})

	r.GET("/hIncrBy", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"hIncrBy": hIncrBy(),
		})
	})

	r.GET("/decrLua", func(c *gin.Context) {

		c.JSON(200, gin.H{
			"decrLua": decrLua(),
		})
	})

	r.GET("/nothing", func(c *gin.Context) {

		c.JSON(200, gin.H{
			"decrLua": decrLua(),
		})
	})

	r.Run()

}

func decrLua() int {
	conn := pool.Get()
	defer conn.Close()

	resp, err := redis.Int(sha1LuaScript.Do(conn, "goods:1", "stock", -1))
	if err != nil {
		log.Fatal(err)
	}
	return resp
}

func hIncrBy() int64 {
	conn := pool.Get()
	defer conn.Close()

	value, err := conn.Do("HINCRBY", "goods:1", "stock", -1)
	if err != nil {
		log.Fatal(err)
	}
	number := value.(int64)
	if number < 0 {
		_, err := conn.Do("HINCRBY", "goods:1", "stock", 1)
		if err != nil {
			log.Fatal(err)
		}
	}

	return number
}

func setNX() interface{} {
	conn := pool.Get()
	defer conn.Close()

	key := fmt.Sprintf("key:%d", rand.Intn(10000000))
	ok, err := conn.Do("SET", key, "123456", "EX", "10", "NX")
	if err != nil {
		fmt.Println("redis set failed:", err)
	}

	return ok
}


func hGetAll() *Goods {
	conn := pool.Get()
	defer conn.Close()

	values, err := redis.Values(conn.Do("HGETALL", "goods:1"))
	if err != nil {
		fmt.Println("HGETALL", err)
	}

	//hashV,_ := redis.StringMap(conn.Do("hgetall", "goods:1"))
	//fmt.Println(hashV)

	var goods Goods
	if err := redis.ScanStruct(values, &goods); err != nil {
		fmt.Println(err)
	}

	return &goods
}